#pragma once

#include "utility/sharedptr.h"
#include "utility/utility.h"
#include "serialise.h"
#include <sstream>

namespace Json
{

class JObject;
class JArray;
class JData;

JObject* Parse(std::ifstream& file);

class JBase
{
public:
	virtual ~JBase() {}
	virtual JObject* GetObject() {return 0;}
	virtual JArray* GetArray() {return 0;}
	virtual std::string* GetString() {return 0;}

	virtual const JObject* GetObject() const {return 0;}
	virtual const JArray* GetArray() const {return 0;}
	virtual const std::string* GetString() const {return 0;}

	virtual void Erase(JBase* p) {}
	virtual JBase* DeepCopy() const {return 0;}

	SERIALISATION_DECL(JBase)
};

class JData : public JBase
{
public:
	JData() {}
	JData(const std::string& string) : m_string(string) {}

	virtual std::string* GetString() {return &m_string;}
	virtual const std::string* GetString() const {return &m_string;}

	JBase* DeepCopy() const {return new JData(*this);}

	SERIALISATION_DECL(JData)

private:
	std::string m_string;
};

class JObject : public JBase
{
public:
	virtual JObject* GetObject() {return this;}

	template <typename T>
	bool GetData(const char* name, T& t) const;

	JObject* GetObject(const char* name);
	JArray* GetArray(const char* name);
	JBase* Get(const char* name);

	const JObject* GetObject(const char* name) const;
	const JArray* GetArray(const char* name) const;
	const JBase* Get(const char* name) const;

	void Insert(const std::string& name, std::string& value);
	void Insert(const std::string& name, JBase* value);

	void Erase(const std::string& name);
	void Erase(JBase* p);

	typedef std::map<std::string, Ut::SharedPtr<JBase> > Map;
	typedef Ut::Range<Map::const_iterator> Range;

	Range GetMembers() const;

	JBase* DeepCopy() const;

	SERIALISATION_DECL_GUID(JObject)
	template <typename Archive> friend void Deserialise(Archive& ar, JObject& t);

private:
	Map m_elements;
};

class JArray : public JBase
{
public:
	virtual JArray* GetArray() {return this;}

	int Size() {return (int) m_elements.size();}

	template <typename T>
	bool GetData(int i, T& t) const;

	JObject* GetObject(int i);
	JArray* GetArray(int i);
	JBase* Get(int i);

	const JObject* GetObject(int i) const;
	const JArray* GetArray(int i) const;
	const JBase* Get(int i) const;

	void PushBack(std::string& value);
	void PushBack(JBase* value);

	void Erase(int i);
	void Erase(JBase* p);

	void Set(int i, JBase* value);

	JBase* DeepCopy() const;

	SERIALISATION_DECL(JArray)

private:
	std::vector<Ut::SharedPtr<JBase> > m_elements;
};

///////////////////////////////////////////////////////////////////

template <typename T>
void ReadValue(const std::string& input, T& t)
{
	std::stringstream s(input);
	s >> t;
}

void ReadValue(const std::string& input, bool& t);
void ReadValue(const std::string& input, std::string& t);

template <typename T>
bool JObject::GetData(const char* name, T& t) const
{
	Map::const_iterator it = m_elements.find(name);

	if (it != m_elements.end() && it->second->GetString())
	{
		ReadValue(*it->second->GetString(), t);
		return true;
	}

	return false;
}

template <typename T>
bool JArray::GetData(int i, T& t) const
{
	if (m_elements[i]->GetString())
	{
		ReadValue(*m_elements[i]->GetString(), t);
		return true;
	}

	return false;
}

void RegisterTypes();

}